package com.bestcem.xm.member.controller.v2;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.domain.web.XmResultJson;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.common.core.uitls.EnumUtil;
import com.bestcem.xm.common.core.uitls.GRpcUtil;
import com.bestcem.xm.common.core.uitls.StringUtil;
import com.bestcem.xm.component.security.context.SecurityContextHolder;
import com.bestcem.xm.component.security.dto.TokenDTO;
import com.bestcem.xm.member.controller.param.HttpDeliveryMembersParam;
import com.bestcem.xm.member.controller.param.HttpListPushMemberParam;
import com.bestcem.xm.member.controller.param.project.FilterProjectRequest;
import com.bestcem.xm.member.controller.v2.param.FilterMembersParam;
import com.bestcem.xm.member.controller.v2.param.HttpDeleteGroupMemberParam;
import com.bestcem.xm.member.controller.vo.DeliveryMemberVO;
import com.bestcem.xm.member.controller.vo.MemberVO;
import com.bestcem.xm.member.controller.vo.ProjectMemberVO;
import com.bestcem.xm.member.enums.DeliveryTypeEnum;
import com.bestcem.xm.member.enums.MemberTypeEnum;
import com.bestcem.xm.member.grpc.service.param.PageParam;
import com.bestcem.xm.member.grpc.service.param.member.ListDeliveryMembersByIdsParam;
import com.bestcem.xm.member.grpc.service.param.member.ListFilterMemberParam;
import com.bestcem.xm.member.grpc.service.param.project.FuzzySearchProjectParam;
import com.bestcem.xm.member.grpc.service.param.project.ListFilterProjectManagerParam;
import com.bestcem.xm.member.service.MemberService;
import com.bestcem.xm.member.service.ProjectService;
import com.bestcem.xm.member.service.dto.MemberDTO;
import com.bestcem.xm.member.service.dto.PageDTO;
import com.bestcem.xm.member.service.dto.ProjectMemberDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author yf.wang <yf.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/8/4
 */
@Slf4j
@RestController
@RequestMapping("member/v2")
public class MemberFilterController {
    /**
     * 预编译SQL过滤正则表达式
     */
    private static final Pattern SQL_PATTERN = Pattern.compile(
        "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|update|and|or|delete"
            + "|insert|trancate|char|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)");

    private void isValid(String source) {
        if (StringUtils.hasText(source)) {
            Matcher matcher = SQL_PATTERN.matcher(source);
            if (matcher.find()) {
                log.error("包含非法字符{}", source);
                throw new BusinessException("排序字段存在sql注入风险,疑似被攻击");
            }
        }
    }

    @Resource
    private MemberService memberService;

    @Resource
    private ProjectService projectService;

    @PostMapping("/filter/members/{type}")
    public XmResultJson filterMembers(@PathVariable String type,
                                      @Valid @RequestBody FilterMembersParam param,
                                      HttpServletRequest request) {

        
        String orgId = SecurityContextHolder.getOrgId();
        
        String userId = SecurityContextHolder.getUserId();
        
        TokenDTO token = SecurityContextHolder.getToken();
        switch (type) {
            case "ind":
                param.setMType(MemberTypeEnum.INDIVIDUAL.getIndex());
                break;
            case "ins":
                param.setMType(MemberTypeEnum.INSTITUTION.getIndex());
                break;
            case "int":
                param.setMType(MemberTypeEnum.INTERIOR.getIndex());
                break;
            default:
                return XmResultJson.fail(-1, "请求失败");
        }
        // 设置默认值
        // 根据修改时间 降序
        if (StrUtil.isEmpty(param.getSortBy())) {
            param.setSortBy("update_time");
            param.setDescending(true);
        } else {
            // 校验注入风险
            isValid(param.getSortBy());
            // 因为前端过来的创建/修改日期字段和数据库不一致，所以需要修改一下
            if (param.getSortBy().equals("created_dt")) {
                param.setSortBy("create_time");
            }
            // updated_dt
            if (param.getSortBy().equals("updated_dt")) {
                param.setSortBy("update_time");
            }

            // memberNo
            if (param.getSortBy().equals("memberNo")) {
                param.setSortBy("member_no");
            }
        }
        // 分页默认是1 size 是10
        if (ObjectUtil.isEmpty(param.getPage())) {
            param.setPage(1);
        }
        if (ObjectUtil.isEmpty(param.getRowsPerPage())) {
            param.setRowsPerPage(10);
        }
        param.setOrgId(orgId);
        param.setUserId(userId);
        param.setSuperRole(token.getSuperRole());
        param.setIsSenior(token.getIsSenior());
        // 过滤联系人
        return memberService.filterMembers(param);
    }

    @PostMapping("/push/members/{memberType}")
    public XmResultJson<PageDTO<MemberDTO>> listPushMembers(@PathVariable String memberType,
                                                                @RequestBody HttpListPushMemberParam param,
                                                                HttpServletRequest request) {
        
        String orgId = SecurityContextHolder.getOrgId();
        
        String userId = SecurityContextHolder.getUserId();
        
        TokenDTO token = SecurityContextHolder.getToken();
        ListFilterMemberParam listFilterMemberParam = new ListFilterMemberParam();
        listFilterMemberParam.setOrgId(orgId);
        listFilterMemberParam.setUserId(userId);
        listFilterMemberParam.setSuperRole(token.getSuperRole());
        listFilterMemberParam.setIsSenior(token.getIsSenior());
        listFilterMemberParam.setMemberTypeEnum(this.mappingMemberTypeEnum(memberType));
        listFilterMemberParam.setDeliveryTypeEnum(EnumUtil.getEnumByName(DeliveryTypeEnum.class, param.getDeliveryType()));
        listFilterMemberParam.setCondition(JSON.toJSONString(param.getCondition()));
        listFilterMemberParam.setAppId(param.getAppId());
        // 默认pageSize
        listFilterMemberParam.setParam(new PageParam(Optional.ofNullable(param.getPage()).orElse(1), 10));

        // 参数校验
        Set<ConstraintViolation<Object>> violations = GRpcUtil.validateParam(param);
        if (!CollectionUtils.isEmpty(violations)) {
            ConstraintViolation<Object> violation = violations.iterator().next();
            return XmResultJson.fail(-1, violation.getMessage());
        }

        if (param.getDeliveryType().equals(DeliveryTypeEnum.WX.getName())) {
            // 微信投放 appId 必须存在
            if (StrUtil.isBlank(param.getAppId())) {
                return XmResultJson.fail(-1, "微信投放,appId不能为空");
            }
        }

        ServiceResult<PageDTO<MemberDTO>> result = memberService.listFilterDeliveryMembers(listFilterMemberParam);
        if (!result.isSuccess()) {
            return XmResultJson.fail(-1, result.getMsg());
        }
        return XmResultJson.success(result.getData());
    }

    /**
     *
     * @param memberType 联系人类型
     * @param regex 模糊匹配参数
     * @param deliveryType 投放类型
     * @param page 分页页数
     * @param pageSize 分页大小
     * @author dc.wang <dc.wang@idiaoyan.com>
     * @date 2021/8/16
     */
    @GetMapping("/push/members/projects")
    public XmResultJson<PageDTO<ProjectMemberVO>> fuzzySearchProjectManager(@RequestParam(name = "mtype")  Integer memberType,
                                                                                 @RequestParam(name = "deliver_search", required = false)  String regex,
                                                                                 @RequestParam(name = "deliver_type")  String deliveryType,
                                                                                 @RequestParam(name = "page", required = false)  Integer page,
                                                                                 @RequestParam(name = "rowsPerPage", required = false) Integer pageSize,
                                                                                 HttpServletRequest request){

        
        String orgId = SecurityContextHolder.getOrgId();
        
         String userId = SecurityContextHolder.getUserId();
        
        TokenDTO token = SecurityContextHolder.getToken();
        FuzzySearchProjectParam param = new FuzzySearchProjectParam();
        param.setOrgId(orgId);
        param.setUserId(userId);
        param.setSuperRole(token.getSuperRole());
        param.setIsSenior(token.getIsSenior());
        param.setMemberTypeEnum(EnumUtil.getEnumByIndex(MemberTypeEnum.class, memberType));
        param.setDeliveryTypeEnum(EnumUtil.getEnumByName(DeliveryTypeEnum.class, deliveryType));
        param.setRegex(regex);
        PageParam pageParam = new PageParam(Optional.ofNullable(page).orElse(1), Optional.ofNullable(pageSize).orElse(20));
        param.setPageParam(pageParam);

        ServiceResult<PageDTO<ProjectMemberDto>> result = projectService.fuzzySearchProjectManager(param);

        if(!result.isSuccess()){
            log.error("请求异常：{},参数：{}", result.getMsg(), param);
            return XmResultJson.fail(-1, result.getMsg());
        }
        List<ProjectMemberVO> data = result.getData().getRows().stream().map(this::convertFrom).collect(Collectors.toList());

        return XmResultJson.success(new PageDTO<>(data, result.getData().getTotal()));
    }

    @PostMapping("/push/members/projects/")
    public XmResultJson<PageDTO<ProjectMemberVO>> listFilterProjects(@RequestBody FilterProjectRequest request,
                                                                          HttpServletRequest httpServletRequest) {
        
        String orgId = SecurityContextHolder.getOrgId();
        String userId = SecurityContextHolder.getUserId();
        TokenDTO token = SecurityContextHolder.getToken();
        ListFilterProjectManagerParam param = new ListFilterProjectManagerParam();
        param.setOrgId(orgId);
        param.setUserId(userId);
        param.setSuperRole(token.getSuperRole());
        param.setIsSenior(token.getIsSenior());
        param.setDeliveryTypeEnum(EnumUtil.getEnumByName(DeliveryTypeEnum.class, request.getDeliveryType()));
        param.setPageParam(new PageParam(Optional.ofNullable(request.getPage()).orElse(1) - 1, 10));
        param.setCondition(JSON.toJSONString(request.getCondition()));

        ServiceResult<PageDTO<ProjectMemberDto>> result = projectService.listFilterProjectManager(param);
        if (!result.isSuccess()) {
            log.error("请求异常：{},参数：{}", result.getMsg(), param);
            return XmResultJson.fail(-1, result.getMsg());
        }

        List<ProjectMemberVO> data = result.getData().getRows().stream().map(this::convertFrom).collect(Collectors.toList());

        return XmResultJson.success(new PageDTO<>(data, result.getData().getTotal()));
    }

    /**
     * 分组筛选删除联系人
     *
     * @param memberType 联系人类型：ind、ins、int
     * @param param 请求参数
     * @return 删除联系人数量
     */
    @DeleteMapping("members/{memberType}")
    public XmResultJson deleteGroupMembers(@PathVariable String memberType,
                                               @Valid @RequestBody HttpDeleteGroupMemberParam param,
                                               HttpServletRequest request) {
        
        String orgId = SecurityContextHolder.getOrgId();
        
         String userId = SecurityContextHolder.getUserId();
        
        TokenDTO token = SecurityContextHolder.getToken();

        MemberTypeEnum memberTypeEnum = mappingMemberTypeEnum(memberType);
        if(MemberTypeEnum.illegalCommonMemberType(memberTypeEnum)){
            return XmResultJson.fail(-1, "不支持的联系人类型");
        }

        param.setOrgId(orgId);
        param.setUserId(userId);
        param.setSuperRole(token.getSuperRole());
        param.setIsSenior(token.getIsSenior());
        param.setMemberType(memberTypeEnum.getIndex());
        ServiceResult<Integer> result = memberService.deleteGroupMembers(param);
        if(!result.isSuccess()){
            log.error("删除联系人异常：{}", param);
            return XmResultJson.fail(-1, result.getMsg());
        }
        return XmResultJson.success(result.getData(), result.getMsg());
    }

    @PostMapping("query/members/")
    public XmResultJson<List<DeliveryMemberVO>> listDeliveryMembers(@RequestBody HttpDeliveryMembersParam requestParam,
                                                                           HttpServletRequest httpServletRequest){

        
        String orgId = SecurityContextHolder.getOrgId();
        String userId = SecurityContextHolder.getUserId();

        ListDeliveryMembersByIdsParam param = new ListDeliveryMembersByIdsParam();
        param.setOrgId(orgId);
        param.setUserId(userId);
        param.setMemberTypeEnum(EnumUtil.getEnumByIndex(MemberTypeEnum.class, requestParam.getMemberType()));
        param.setMemberIds(requestParam.getMemberIds());
        ServiceResult<List<MemberDTO>> result = memberService.listDeliveryMembersByIds(param);
        if(!result.isSuccess()){
            return XmResultJson.fail(-1, result.getMsg());
        }

        List<DeliveryMemberVO> memberVos = result.getData().stream().map(this::convert).collect(Collectors.toList());
        return XmResultJson.success(memberVos);
    }

    private DeliveryMemberVO convert(MemberDTO memberDTO){
        DeliveryMemberVO res = new DeliveryMemberVO();
        res.setId(memberDTO.getId());
        res.setType(memberDTO.getMemberType());
        res.setName(memberDTO.getName());
        res.setRowName(memberDTO.getName());
        res.setMobile(memberDTO.getMobile());
        res.setEmail(memberDTO.getEmail());
        res.setInstituteName(memberDTO.getInstituteName());
        res.setOutNo(memberDTO.getOutNo());
        return res;
    }

    private ProjectMemberVO convertFrom(ProjectMemberDto dto){
        ProjectMemberVO res = new ProjectMemberVO();
        res.setId(dto.getProjectId());
        res.setName(dto.getProjectName());
        res.setProjectManager(dto.getProjectManagerName());
        res.setProjectManagerMobile(dto.getProjectManagerPhone());

        if(dto.getMemberDTO() != null){
            MemberDTO memberDTO = dto.getMemberDTO();
            MemberVO memberVO = new MemberVO();
            memberVO.setName(memberDTO.getName());
            memberVO.setInstituteName(memberDTO.getInstituteName());
            memberVO.setMemberId(memberDTO.getId());
            memberVO.setId(memberDTO.getId());
            memberVO.setMemberType(memberDTO.getMemberType());
            res.setMember(memberVO);
        }
        return res;
    }

    private MemberTypeEnum mappingMemberTypeEnum(String memberType){
        if(StringUtil.isNullOrEmpty(memberType)){
            return null;
        }
        switch (memberType){
            case "ind":
                return MemberTypeEnum.INDIVIDUAL;
            case "ins":
                return MemberTypeEnum.INSTITUTION;
            case "int":
                return MemberTypeEnum.INTERIOR;
            default:
                return null;
        }
    }
}
